#include "PrecompiledHeader.h"
#include "IRendererImpl.h"

/*--------------------------------------------------------------------------*/

IRendererImpl::IRendererImpl()
{
	this->m_textureBuffers = NULL;
	this->m_uniformBuffers = NULL;
	this->m_primitivesMat = NULL;
	this->m_matricesDirtyFlags = 0;

	this->InitializeUniformBuffers();
	this->InitializeTextureBuffers();
	this->InitializeFont();

	this->m_uiRenderPhase = this->CreateRenderPhase("UserInterface", 99999, ERenderPhaseMethod::BackbufferColorOnly, NULL, NULL, NULL);
}

/*--------------------------------------------------------------------------*/

IRendererImpl::~IRendererImpl()
{
	/*--------------------------------------------------------------------------*/

	for (uint i = 0; i < ETexture::Count; ++i)
	{
		ROpenGL::DeleteTexture(&this->m_textureBuffers[i]);
	}

	delete [] this->m_textureBuffers;

	/*--------------------------------------------------------------------------*/

	ROpenGL::DeleteBuffer(&this->m_lettersInstancedData);
	ROpenGL::DeleteVertexArray(&this->m_lettersInstancingVao);

	/*--------------------------------------------------------------------------*/

	for (auto iter = this->m_renderPhases.begin(); iter != this->m_renderPhases.end(); ++iter)
	{
		delete *iter;
	}

	/*--------------------------------------------------------------------------*/

	for (uint i = 0; i < EUbo::Count; ++i)
	{
		ROpenGL::DeleteUniformBuffer(&this->m_uniformBuffers[i]);
	}

	delete [] this->m_uniformBuffers;

	/*--------------------------------------------------------------------------*/
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::InitializeUniformBuffers()
{
	this->m_uniformBuffers = new GLUniformBuffer*[EUbo::Count];

	for (uint i = 0; i < EUbo::Count; ++i)
	{
		this->m_uniformBuffers[i] = ROpenGL::GenUniformBuffer(&g_uniformBuffersDesc[i]);
	}
}

/*--------------------------------------------------------------------------*/

GLUniformBuffer* IRendererImpl::GetUniformBuffer(EUbo::Name name)
{
	return this->m_uniformBuffers[name];
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::InitializeTextureBuffers()
{
	this->m_textureBuffers = new GLTexture*[ETexture::Count];

	for (uint i = 0; i < ETexture::Count; ++i)
	{
		GLTextureDesc desc = g_textureBuffersDesc[i];

		if (desc.width == 0 || desc.height == 0)
		{
			desc.width = g_Settings->GetResolutioni().x;
			desc.height = g_Settings->GetResolutioni().y;
		}

		this->m_textureBuffers[i] = ROpenGL::GenTexture(&desc);

		ROpenGL::SetSampler(desc.name, this->m_textureBuffers[i]);
	}

	this->m_ppfx_a_FBO = ROpenGL::GenFramebuffer("PPFX_A_FBO");
	ROpenGL::SetFramebufferColorAttachment(this->m_ppfx_a_FBO, 0, this->m_textureBuffers[ETexture::PPFX_A]);
	ROpenGL::ValidateFramebuffer(this->m_ppfx_a_FBO);

	this->m_ppfx_b_FBO = ROpenGL::GenFramebuffer("PPFX_B_FBO");
	ROpenGL::SetFramebufferColorAttachment(this->m_ppfx_b_FBO, 0, this->m_textureBuffers[ETexture::PPFX_B]);
	ROpenGL::ValidateFramebuffer(this->m_ppfx_b_FBO);
}

/*--------------------------------------------------------------------------*/

GLTexture* IRendererImpl::GetTextureBuffer(ETexture::Name name)
{
	return this->m_textureBuffers[name];
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::ProcessPhases()
{
	for (auto iter = this->m_renderPhases.begin(); iter != this->m_renderPhases.end(); ++iter)
	{
		ViaMain::Timestamp("Processing IRenderPhase %s", (*iter)->GetName().c_str());
		(*iter)->Process();
	}
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::SetModelMatrix(const glm::mat4& modelmatrix)
{
	this->m_modelMatrix = modelmatrix;
	this->m_matricesDirtyFlags |= EMatrixDirtyFlag::ModelMatrixChanged;
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::SetViewMatrix(const glm::mat4& viewMatrix)
{
	this->m_viewMatrix = viewMatrix;
	this->m_matricesDirtyFlags |= EMatrixDirtyFlag::ViewMatrixChanged;
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::SetProjectionMatrix(const glm::mat4& projectionMatrix)
{
	this->m_projectionMatrix = projectionMatrix;
	this->m_matricesDirtyFlags |= EMatrixDirtyFlag::ProjectionMatrixChanged;
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::UpdateModelMatrices()
{
	if (this->m_matricesDirtyFlags & EMatrixDirtyFlag::ModelMatrixChanged)
	{
		ROpenGL::SetUniformBufferValue(this->GetUniformBuffer(EUbo::Matrices), EUboMatrices::ModelMatrix, &this->m_modelMatrix);
	}

	if (this->m_matricesDirtyFlags & EMatrixDirtyFlag::ModelMatrixChanged || this->m_matricesDirtyFlags & EMatrixDirtyFlag::MVPChanged)
	{
		this->m_modelViewMatrix = this->m_viewMatrix * this->m_modelMatrix;
		this->m_modelViewProjectionMatrix = this->m_projectionMatrix * this->m_modelViewMatrix;

		ROpenGL::SetUniformBufferValue(this->GetUniformBuffer(EUbo::Matrices), EUboMatrices::ModelViewMatrix, &this->m_modelViewMatrix);
		ROpenGL::SetUniformBufferValue(this->GetUniformBuffer(EUbo::Matrices), EUboMatrices::ModelViewProjectionMatrix, &this->m_modelViewProjectionMatrix);
	}

	this->m_matricesDirtyFlags &= ~EMatrixDirtyFlag::ModelMatrixChanged;
	this->m_matricesDirtyFlags &= ~EMatrixDirtyFlag::MVPChanged;
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::UpdateCameraMatrices()
{
	if (this->m_matricesDirtyFlags & EMatrixDirtyFlag::ProjectionMatrixChanged)
	{
		this->m_invProjectionMatrix = glm::inverse(this->m_projectionMatrix);

		ROpenGL::SetUniformBufferValue(this->GetUniformBuffer(EUbo::Matrices), EUboMatrices::ProjectionMatrix, &this->m_projectionMatrix);
		ROpenGL::SetUniformBufferValue(this->GetUniformBuffer(EUbo::Matrices), EUboMatrices::InvProjectionMatrix, &this->m_invProjectionMatrix);
	}

	if (this->m_matricesDirtyFlags & EMatrixDirtyFlag::ViewMatrixChanged)
	{
		ROpenGL::SetUniformBufferValue(this->GetUniformBuffer(EUbo::Matrices), EUboMatrices::ViewMatrix, &this->m_viewMatrix);
	}

	if (this->m_matricesDirtyFlags & EMatrixDirtyFlag::ProjectionMatrixChanged || this->m_matricesDirtyFlags & EMatrixDirtyFlag::ViewMatrixChanged)
	{
		this->m_viewProjectionMatrix = this->m_projectionMatrix * this->m_viewMatrix;
		this->m_invViewProjectionMatrix = glm::inverse(this->m_viewProjectionMatrix);

		ROpenGL::SetUniformBufferValue(this->GetUniformBuffer(EUbo::Matrices), EUboMatrices::ViewProjectionMatrix, &this->m_viewProjectionMatrix);
		ROpenGL::SetUniformBufferValue(this->GetUniformBuffer(EUbo::Matrices), EUboMatrices::InvViewProjectionMatrix, &this->m_invViewProjectionMatrix);
	}

	this->m_matricesDirtyFlags &= ~EMatrixDirtyFlag::ProjectionMatrixChanged;
	this->m_matricesDirtyFlags &= ~EMatrixDirtyFlag::ViewMatrixChanged;

	this->m_matricesDirtyFlags |= EMatrixDirtyFlag::MVPChanged;
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::SaveScreenshot(const std::string& path)
{
	ROpenGL::SaveScreenshot(path, glm::ivec2(0), g_Settings->GetResolutioni());
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::SwapBuffers()
{
	g_Sdl->SwapBuffers();
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::ShowSplashscreen(ESplashscreen::Type type, const std::string& label, GLTexture* texture, Font* font, float progress)
{
	switch (type)
	{
	case ESplashscreen::WindowTitle:
	{
		AssertModule(g_Sdl);

		ROpenGL::UnbindFramebuffer();
		ROpenGL::ClearBuffers();

		g_Sdl->SetWindowTitle(label);
		break;
	}

	case ESplashscreen::Text:
	{
		AssertModule(g_Hud);

		ROpenGL::UnbindFramebuffer();
		ROpenGL::ClearBuffers();

		g_Renderer->DrawText2D(font, label, glm::vec2(30.0f, 30.0f), Color::White);

		this->SwapBuffers();
		break;
	}

	case ESplashscreen::Text_Image:
	{
		AssertModule(g_Hud);

		ROpenGL::UnbindFramebuffer();
		ROpenGL::ClearBuffers();

		glm::ivec2 size = g_Settings->GetResolutioni();
		int diff = (size.x - size.y + 50) / 2;

		g_Renderer->DrawRectangle2D(glm::vec2(diff, 50), glm::vec2(size.x - diff, size.y), texture, Color::Green);
		g_Renderer->DrawText2D(font, label, glm::vec2(diff, 30.0f), Color::White);

		this->SwapBuffers();
		break;
	}

	case ESplashscreen::Text_Image_Progress:
	{
		AssertModule(g_Hud);

		ROpenGL::UnbindFramebuffer();
		ROpenGL::ClearColor(Color::Black);
		ROpenGL::ClearBuffers();

		g_Renderer->DrawText2D(font, label, glm::vec2(100.0f, 100.0f), Color::White);

		this->SwapBuffers();
		break;
	}

	default:
	{
		AssertLogC(true, "Unknown ESplashscreen value");
	}
	}
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::UpdateUBOs()
{
	ROpenGL::SetUniformBufferValue(this->m_uniformBuffers[EUbo::General], EUboGeneral::MousePosition, (void*) &g_Mouse->GetPosition());

	glm::vec2 invScrRes = 1.0f / g_Settings->GetResolutionf();
	ROpenGL::SetUniformBufferValue(this->m_uniformBuffers[EUbo::General], EUboGeneral::ScreenResolution, (void*) &g_Settings->GetResolutionf());
	ROpenGL::SetUniformBufferValue(this->m_uniformBuffers[EUbo::General], EUboGeneral::InvScreenResolution, (void*) &invScrRes);

	float ScrAsp = g_Settings->GetResolutionf().x * invScrRes.y;
	ROpenGL::SetUniformBufferValue(this->m_uniformBuffers[EUbo::General], EUboGeneral::ScreenAspectRatio, (void*) &ScrAsp);
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::DrawRectangle2D(const glm::vec2& p1, const glm::vec2& p2, const glm::vec2& p3, const glm::vec2& p4, GLTexture* texture, const Color& color)
{
	BasicVertex* v = ROpenGL::MapPrimitiveContainer(4);
	v[0].texcoord = glm::vec2(0.0f, 0.0f);
	v[0].normal = glm::vec3(0.0f, 0.0f, 1.0f);
	v[0].position = glm::vec3(p1, 0.0f);
	v[0].color = color;

	v[1].texcoord = glm::vec2(1.0f, 0.0f);
	v[1].normal = glm::vec3(0.0f, 0.0f, 1.0f);
	v[1].position = glm::vec3(p2, 0.0f);
	v[1].color = color;

	v[2].texcoord = glm::vec2(1.0f, 1.0f);
	v[2].normal = glm::vec3(0.0f, 0.0f, 1.0f);
	v[2].position = glm::vec3(p3, 0.0f);
	v[2].color = color;

	v[3].texcoord = glm::vec2(0.0f, 1.0f);
	v[3].normal = glm::vec3(0.0f, 0.0f, 1.0f);
	v[3].position = glm::vec3(p4, 0.0f);
	v[3].color = color;

	ROpenGL::UnmapPrimitiveContainer();

	ROpenGL::SetSampler("texture0", texture);

	if (this->m_primitivesMat == NULL)
	{
		this->m_primitivesMat = g_Material->GetMaterial("mat_primitives");
	}

	if (this->m_primitivesMat)
	{
		if (texture)
		{
			g_Material->BindMaterial(this->m_primitivesMat, EMaterialTechnique::Default);
		}
		else
		{
			g_Material->BindMaterial(this->m_primitivesMat, EMaterialTechnique::Primitive_NoTexture);
		}

		ROpenGL::DrawPrimitive(GL_QUADS, 4);
	}
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::DrawRectangle2D(const glm::vec2& p1, const glm::vec2& p2, GLTexture* texture, const Color& color)
{
	BasicVertex* v = ROpenGL::MapPrimitiveContainer(4);
	v[0].texcoord = glm::vec2(0.0f, 0.0f);
	v[0].normal = glm::vec3(0.0f, 0.0f, 1.0f);
	v[0].position.x = p1.x;
	v[0].position.y = p1.y;
	v[0].color = color;

	v[1].texcoord = glm::vec2(1.0f, 0.0f);
	v[1].normal = glm::vec3(0.0f, 0.0f, 1.0f);
	v[1].position.x = p2.x;
	v[1].position.y = p1.y;
	v[1].color = color;

	v[2].texcoord = glm::vec2(1.0f, 1.0f);
	v[2].normal = glm::vec3(0.0f, 0.0f, 1.0f);
	v[2].position.x = p2.x;
	v[2].position.y = p2.y;
	v[2].color = color;

	v[3].texcoord = glm::vec2(0.0f, 1.0f);
	v[3].normal = glm::vec3(0.0f, 0.0f, 1.0f);
	v[3].position.x = p1.x;
	v[3].position.y = p2.y;
	v[3].color = color;

	ROpenGL::UnmapPrimitiveContainer();

	if (this->m_primitivesMat == NULL)
	{
		this->m_primitivesMat = g_Material->GetMaterial("mat_primitives");
	}

	if (this->m_primitivesMat)
	{
		if (texture)
		{
			g_Material->BindMaterial(this->m_primitivesMat, EMaterialTechnique::Default);
		}
		else
		{
			g_Material->BindMaterial(this->m_primitivesMat, EMaterialTechnique::Primitive_NoTexture);
		}

		ROpenGL::SetSampler("texture0", texture);
		ROpenGL::DrawPrimitive(GL_QUADS, 4);
	}
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::DrawTriangle2D(const glm::vec2& p1, const glm::vec2& p2, const glm::vec2& p3, GLTexture* texture, const Color& color)
{
	BasicVertex* v = ROpenGL::MapPrimitiveContainer(3);
	v[0].texcoord = glm::vec2(0.0f, 0.0f);
	v[0].normal = glm::vec3(0.0f, 0.0f, 1.0f);
	v[0].position = glm::vec3(p1, 0.0f);
	v[0].color = color;

	v[1].texcoord = glm::vec2(1.0f, 0.0f);
	v[1].normal = glm::vec3(0.0f, 0.0f, 1.0f);
	v[1].position = glm::vec3(p2, 0.0f);
	v[1].color = color;

	v[2].texcoord = glm::vec2(1.0f, 1.0f);
	v[2].normal = glm::vec3(0.0f, 0.0f, 1.0f);
	v[2].position = glm::vec3(p3, 0.0f);;
	v[2].color = color;

	ROpenGL::UnmapPrimitiveContainer();

	ROpenGL::SetSampler("texture0", texture);

	if (this->m_primitivesMat == NULL)
	{
		this->m_primitivesMat = g_Material->GetMaterial("mat_primitives");
	}

	if (this->m_primitivesMat)
	{
		if (texture)
		{
			g_Material->BindMaterial(this->m_primitivesMat, EMaterialTechnique::Default);
		}
		else
		{
			g_Material->BindMaterial(this->m_primitivesMat, EMaterialTechnique::Primitive_NoTexture);
		}

		ROpenGL::DrawPrimitive(GL_TRIANGLES, 3);
	}
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::DrawLine2D(const glm::vec2& p1, const glm::vec2& p2, GLTexture* texture, const Color& color)
{
	BasicVertex* v = ROpenGL::MapPrimitiveContainer(2);
	v[0].texcoord = glm::vec2(0.0f, 0.0f);
	v[0].normal = glm::vec3(0.0f, 0.0f, 1.0f);
	v[0].position = glm::vec3(p1, 0.0f);
	v[0].color = color;

	v[1].texcoord = glm::vec2(1.0f, 0.0f);
	v[1].normal = glm::vec3(0.0f, 0.0f, 1.0f);
	v[1].position = glm::vec3(p2, 0.0f);
	v[1].color = color;

	ROpenGL::UnmapPrimitiveContainer();

	ROpenGL::SetSampler("texture0", texture);

	if (this->m_primitivesMat == NULL)
	{
		this->m_primitivesMat = g_Material->GetMaterial("mat_primitives");
	}

	if (this->m_primitivesMat)
	{
		if (texture)
		{
			g_Material->BindMaterial(this->m_primitivesMat, EMaterialTechnique::Default);
		}
		else
		{
			g_Material->BindMaterial(this->m_primitivesMat, EMaterialTechnique::Primitive_NoTexture);
		}

		ROpenGL::DrawPrimitive(GL_LINES, 2);
	}
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::DrawText2D(const Font* font, const std::string& content, const glm::vec2& position, const Color& color, EFontStyle::Type style)
{
	if (this->m_fontMaterial == NULL)
	{
		this->m_fontMaterial = g_Material->GetMaterial("mat_font");
	}

	if (this->res_defaultFont == NULL)
	{
		g_Resources->Get("default", &this->res_defaultFont);
		AssertLogC(this->res_defaultFont == NULL, "Default font does not exist.");
	}

	if (font == NULL)
	{
		font = this->res_defaultFont;

		AssertLogC(this->res_defaultFont == NULL, "Default font is NULL.");

		if (font == NULL)
		{
			return;
		}
	}

	uint length = content.length();
	float margin = 0.0f;
	uint bufferPos = 0;

	float inv_cache_width = 1.0f / font->GetTexture()->size.x;
	float cache_height = (float) font->GetTexture()->size.y;
	const uint* widths = font->GetLettersWidths();

	glm::vec2 outlineRadius = glm::vec2(0.04f, 0.02f);
	float outlineFactor = 0.5f;

	ROpenGL::SetUniformBufferValue(g_Renderer->GetUniformBuffer(EUbo::Font), EUboFont::FontColor, (void*) &color);
	ROpenGL::SetUniformBufferValue(g_Renderer->GetUniformBuffer(EUbo::Font), EUboFont::OutlineRadius, (void*) &outlineRadius);
	ROpenGL::SetUniformBufferValue(g_Renderer->GetUniformBuffer(EUbo::Font), EUboFont::OutlineFactor, (void*) &outlineFactor);

	ROpenGL::BindVertexArray(ROpenGL::GetQuadVao());

	FontData fontData;

	for (uint i = 0; i < length; ++i)
	{
		uchar char_to_render = content[i];

		if (char_to_render == ' ')
		{
			margin += cache_height;
			continue;
		}

		uchar char_in_cache = char_to_render - 32;

		uint next_char_start = widths[char_in_cache + 1];
		uint char_start = widths[char_in_cache];

		float width = (float)(next_char_start - char_start);
		float left = (float) char_start;

		float lcw = left * inv_cache_width;
		float lwcw = (left + width) * inv_cache_width;

		fontData.m_position = glm::vec2(margin +  position.x, position.y);
		fontData.m_size = glm::vec2(width, cache_height);
		fontData.m_texcoord = glm::vec2(lcw, lwcw);

		margin += width;

		ROpenGL::CopyDataToShadow(this->m_lettersInstancedData, &fontData, sizeof(FontData), bufferPos * sizeof(FontData));
		++bufferPos;

		AssertLogC(bufferPos == CHUD_SIZE_OF_LETTERS_CACHE, "Cannot render label %s. It is too long!", content.c_str());

		if (margin > g_Settings->GetResolutionf().x)
		{
			break;
		}
	}

	ROpenGL::FlushShadowBuffer(this->m_lettersInstancedData);

	switch (style)
	{
	case EFontStyle::Normal:
	{
		g_Material->BindMaterial(this->m_fontMaterial, EMaterialTechnique::Font_Normal);
		break;
	}

	case EFontStyle::Outline:
	{
		g_Material->BindMaterial(this->m_fontMaterial, EMaterialTechnique::Font_Outline);
		break;
	}

	default:
	{
		AssertLogC(true, "Unknown EFontStyle");
	}
	}

	ROpenGL::SetSampler("fontTexture", font->GetTexture());
	ROpenGL::BindVertexArray(this->m_lettersInstancingVao);
	ROpenGL::DrawVertexArray(bufferPos);
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::InitializeFont()
{
	this->ubo_font = ROpenGL::GetUniformBuffer("ubo_font");
	AssertLogC(this->ubo_font == NULL, "UBO not found!");

	this->res_defaultFont = NULL;
	this->m_fontMaterial = NULL;

	GLVertexBuffer* vbpos = ROpenGL::GenBuffer("LETTER_POS", EVertexDeclaration::Position0, 4);
	glm::vec3* v = (glm::vec3*) ROpenGL::MapBuffer(vbpos);
	v[0] = glm::vec3(0.0f, 0.0f, 0.0f);
	v[1] = glm::vec3(1.0f, 0.0f, 0.0f);
	v[2] = glm::vec3(1.0f, 1.0f, 0.0f);
	v[3] = glm::vec3(0.0f, 1.0f, 0.0f);
	ROpenGL::UnmapBuffer(vbpos);

	GLVertexBuffer* vbind = ROpenGL::GenBuffer("IB_FOR_LETTERS", EVertexDeclaration::Indices, 6);
	uint* vi = (uint*) ROpenGL::MapBuffer(vbind);
	vi[0] = 0;
	vi[1] = 1;
	vi[2] = 2;
	vi[3] = 0;
	vi[4] = 2;
	vi[5] = 3;
	ROpenGL::UnmapBuffer(vbind);

	this->m_lettersInstancedData = ROpenGL::GenBuffer("INSTANCEDATA_FOR_LETTERS", EVertexDeclaration::FontData, CHUD_SIZE_OF_LETTERS_CACHE, GL_DYNAMIC_DRAW);
	ROpenGL::UnbindBuffers();

	/*--------------------------------------------------------------------------*/

	this->m_lettersInstancingVao = ROpenGL::GenVertexArray("VAO_FOR_LETTERS", GL_TRIANGLES, 6);
	ROpenGL::BindVertexArray(this->m_lettersInstancingVao);

	ROpenGL::AttachBuffer(vbpos);
	ROpenGL::AttachBuffer(vbind);
	ROpenGL::AttachBuffer(this->m_lettersInstancedData);

	ROpenGL::UniqueBufferOwner(this->m_lettersInstancingVao, vbpos);
	ROpenGL::UniqueBufferOwner(this->m_lettersInstancingVao, vbind);

	ROpenGL::UnbindVertexArray();
}

/*--------------------------------------------------------------------------*/

IRenderPhase* IRendererImpl::CreateRenderPhase(const std::string& name, int priority, ERenderPhaseMethod::Method method, SceneInstance* sceneinstance, ICamera* camera, GLTexture* output)
{
	IRenderPhase* phase = NULL;

	switch (method)
	{
	case ERenderPhaseMethod::DeferredShading:
	{
		phase = new DeferredShadingPhase();
		break;
	}

	case ERenderPhaseMethod::BackbufferColorOnly:
	{
		phase = new BackbufferColorOnlyPhase();
		break;
	}

	case ERenderPhaseMethod::NormalRendering:
	{
		phase = new NormalRenderingPhase();
		break;
	}

	default:
	{
		AssertLogC(true, "Unknown ERenderPhaseMethod");
	}
	}

	AssertLogC(phase == NULL, "Unable to create IRenderPhase");

	phase->SetCamera(camera, 0);
	phase->SetScene(sceneinstance);
	phase->SetName(name);
	phase->SetPriority(priority);

	this->m_renderPhases.push_back(phase);
	this->m_renderPhases.back()->Initialize(output);
	this->SortPhases();

	return phase;
}

/*--------------------------------------------------------------------------*/

bool SortPhasesByPriority(const IRenderPhase* d1, const IRenderPhase* d2)
{
	return d1->GetPriority() < d2->GetPriority();
}

void IRendererImpl::SortPhases()
{
	std::sort(this->m_renderPhases.begin(), this->m_renderPhases.end(), SortPhasesByPriority);
}

/*--------------------------------------------------------------------------*/

IRenderPhase* IRendererImpl::GetUIRenderPhase()
{
	return m_uiRenderPhase;
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::BindPPFXA()
{
	ROpenGL::BindFramebuffer(this->m_ppfx_a_FBO);
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::BindPPFXB()
{
	ROpenGL::BindFramebuffer(this->m_ppfx_b_FBO);
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::DeleteRenderPhase(SceneInstance* sceneinstance)
{
	bool deleted = false;

	for (auto i = this->m_renderPhases.begin(); i != this->m_renderPhases.end();)
	{
		if ((*i)->GetScene() == sceneinstance)
		{
			delete *i;
			i = this->m_renderPhases.erase(i);
		}
		else
		{
			++i;
		}
	}
}

/*--------------------------------------------------------------------------*/

void IRendererImpl::DrawLine3D(const glm::vec3& p1, const glm::vec3& p2, GLTexture* texture, const Color& color /*= Color::White*/)
{
	BasicVertex* v = ROpenGL::MapPrimitiveContainer(2);
	v[0].texcoord = glm::vec2(0.0f, 0.0f);
	v[0].normal = glm::vec3(0.0f, 0.0f, 1.0f);
	v[0].position = p1;
	v[0].color = color;

	v[1].texcoord = glm::vec2(1.0f, 0.0f);
	v[1].normal = glm::vec3(0.0f, 0.0f, 1.0f);
	v[1].position = p2;
	v[1].color = color;

	ROpenGL::UnmapPrimitiveContainer();

	ROpenGL::SetSampler("texture0", texture);

	if (this->m_primitivesMat == NULL)
	{
		this->m_primitivesMat = g_Material->GetMaterial("mat_primitives");
	}

	if (this->m_primitivesMat)
	{
		if (texture)
		{
			g_Material->BindMaterial(this->m_primitivesMat, EMaterialTechnique::Default3D);
		}
		else
		{
			g_Material->BindMaterial(this->m_primitivesMat, EMaterialTechnique::Primitive_NoTexture_3D);
		}

		ROpenGL::DrawPrimitive(GL_LINES, 2);
	}
}

/*--------------------------------------------------------------------------*/